﻿using System;
using System.IO;

namespace ExtendedStream
{
    /// <summary>
    /// This class can be used to write a big data Stream to some Seperated Stream.
    /// </summary>
    public class UnlimitedWriteStream:Stream
    {
        #region Fields
        private long _index;
        private long _lastStreamWroteSize;
        private readonly IUnlimitedStreamProvider _unlimitedStreamProvider;
        private IUnlimitedStreamProviderData _lastUnlimitedStreamProviderData; 
        #endregion

        #region Constructors
        public UnlimitedWriteStream(IUnlimitedStreamProvider unlimitedStreamProvider)
        {
            _lastStreamWroteSize = 0;
            _unlimitedStreamProvider = unlimitedStreamProvider;
        } 
        #endregion

        #region Overrides of Stream

        public override void Flush()
        {
        }

        public override long Seek(long offset, SeekOrigin origin)
        {
            throw new NotSupportedException();
        }

        public override void SetLength(long value)
        {
            throw new NotSupportedException();
        }

        public override int Read(byte[] buffer, int offset, int count)
        {
            throw new NotSupportedException();
        }

        public override void Write(byte[] buffer, int offset, int count)
        {
            while (count>0)
            {
                if (_lastUnlimitedStreamProviderData == null)
                {
                    _lastUnlimitedStreamProviderData = _unlimitedStreamProvider.GetUnlimitedData(_index);
                    if (_lastUnlimitedStreamProviderData.Header != null)
                    {
                        _lastUnlimitedStreamProviderData.Header.CopyTo(_lastUnlimitedStreamProviderData.Destination);
                    }

                }
                if (_lastUnlimitedStreamProviderData.LimitationType == LimitationType.Number)
                {
                    long limitation = _lastUnlimitedStreamProviderData.UseLimitation
                        ? _lastUnlimitedStreamProviderData.Limitation
                        : _unlimitedStreamProvider.Limitation;
                    if (_lastStreamWroteSize + count >= limitation)
                    {
                        int writeSize = (int)(limitation - _lastStreamWroteSize);
                        _lastUnlimitedStreamProviderData.Destination.Write(buffer, offset, writeSize);
                        offset += writeSize;
                        count -= writeSize;
                        _lastStreamWroteSize = 0;
                        if (_lastUnlimitedStreamProviderData.Tail != null)
                            _lastUnlimitedStreamProviderData.Tail.CopyTo(_lastUnlimitedStreamProviderData.Destination);
                        _unlimitedStreamProvider.OnUnlimitedStreamProviderDataWrote(_lastUnlimitedStreamProviderData, _index);
                        _index++;
                        _lastUnlimitedStreamProviderData = null;
                    }
                    else
                    {
                        _lastUnlimitedStreamProviderData.Destination.Write(buffer, offset, count);
                        _lastStreamWroteSize += count;
                        return;
                    }                    
                }
                else
                {
                    int tryWriteDataSize = _lastUnlimitedStreamProviderData.TryWriteData(buffer, offset, count);
                    if (tryWriteDataSize < count)
                    {
                        int writeSize = tryWriteDataSize;
                        _lastUnlimitedStreamProviderData.Destination.Write(buffer, offset, writeSize);
                        offset += writeSize;
                        count -= writeSize;
                        _lastStreamWroteSize = 0;
                        if (_lastUnlimitedStreamProviderData.Tail != null)
                            _lastUnlimitedStreamProviderData.Tail.CopyTo(_lastUnlimitedStreamProviderData.Destination);
                        _unlimitedStreamProvider.OnUnlimitedStreamProviderDataWrote(_lastUnlimitedStreamProviderData, _index);
                        _index++;
                        _lastUnlimitedStreamProviderData = null;
                        
                    }
                    else
                    {
                        _lastUnlimitedStreamProviderData.Destination.Write(buffer, offset, count);
                        _lastStreamWroteSize += count;
                        return;                        
                    }
                }
            }
            
        }
        
        public override bool CanRead
        {
            get { return false; }
        }

        public override bool CanSeek
        {
            get { return false; }
        }

        public override bool CanWrite
        {
            get { return true; }
        }

        public override long Length
        {
            get { throw new NotSupportedException(); }
        }

        public override long Position { get; set; }

        #region Overrides of Stream

        public override void Close()
        {
            base.Close();
            if(_lastUnlimitedStreamProviderData != null)
                _unlimitedStreamProvider.OnUnlimitedStreamProviderDataWrote(_lastUnlimitedStreamProviderData,_index);
        }

        #endregion

        #endregion
    }
}
